package tests

import (
	"testing"

	"gitcode.com/deyiyangyang/showen-lang/typ"
)

// TestBasicExpressions tests basic expressions
func TestBasicExpressions(t *testing.T) {
	env := typ.NewEnvironment()
	typ.DefineBuiltins(env)

	testCases := []struct {
		input    string
		expected string
	}{
		{"(+ 1 2 3)", "6"},
		{"(* 2 3 4)", "24"},
		{"(- 10 5)", "5"},
		{"(/ 10 2)", "5"},
		{"(+ 1)", "1"},
		{"(* 5)", "5"},
		{"(- 7)", "-7"},
	}

	for _, tc := range testCases {
		result := EvaluateExpression(t, tc.input, env)
		if result != tc.expected {
			t.Errorf("For input %s, expected %s, but got %s", tc.input, tc.expected, result)
		}
	}
}

// TestNestedExpressions tests nested expressions
func TestNestedExpressions(t *testing.T) {
	env := typ.NewEnvironment()
	typ.DefineBuiltins(env)

	testCases := []struct {
		input    string
		expected string
	}{
		{"(- (+ 2 3) (* 1 2))", "3"},
		{"(+ (* 2 3) (/ 10 5))", "8"},
		{"(* (+ 1 2) (- 5 3))", "6"},
	}

	for _, tc := range testCases {
		result := EvaluateExpression(t, tc.input, env)
		if result != tc.expected {
			t.Errorf("For input %s, expected %s, but got %s", tc.input, tc.expected, result)
		}
	}
}

// TestVariableAndFunctionDefinitions tests variable and function definitions
func TestVariableAndFunctionDefinitions(t *testing.T) {
	env := typ.NewEnvironment()
	typ.DefineBuiltins(env)

	// Define variable
	result := EvaluateExpression(t, "(define x 10)", env)
	if result != "x" {
		t.Errorf("When defining variable, expected to return x, but got %s", result)
	}

	// Use variable
	result = EvaluateExpression(t, "x", env)
	if result != "10" {
		t.Errorf("Expected value of variable x to be 10, but got %s", result)
	}

	// Define function
	result = EvaluateExpression(t, "(define (square x) (* x x))", env)
	if result != "square" {
		t.Errorf("When defining function, expected to return square, but got %s", result)
	}

	// Call function
	result = EvaluateExpression(t, "(square 5)", env)
	if result != "25" {
		t.Errorf("Expected (square 5) to return 25, but got %s", result)
	}

	// Define factorial function
	result = EvaluateExpression(t, "(define (factorial n) (if (<= n 1) 1 (* n (factorial (- n 1)))))" , env)
	if result != "factorial" {
		t.Errorf("When defining factorial function, expected to return factorial, but got %s", result)
	}

	// Call factorial function
	result = EvaluateExpression(t, "(factorial 5)", env)
	if result != "120" {
		t.Errorf("Expected (factorial 5) to return 120, but got %s", result)
	}
}

// TestListOperations tests list operations
func TestListOperations(t *testing.T) {
	env := typ.NewEnvironment()
	typ.DefineBuiltins(env)

	// Define list
	result := EvaluateExpression(t, "(define lst (list 1 2 3 4 5))", env)
	if result != "lst" {
		t.Errorf("When defining list, expected to return lst, but got %s", result)
	}

	// Use list
	result = EvaluateExpression(t, "lst", env)
	if result != "(1 2 3 4 5)" {
		t.Errorf("Expected list value to be (1 2 3 4 5), but got %s", result)
	}

	// car operation
	result = EvaluateExpression(t, "(car lst)", env)
	if result != "1" {
		t.Errorf("Expected (car lst) to return 1, but got %s", result)
	}

	// cdr operation
	result = EvaluateExpression(t, "(cdr lst)", env)
	if result != "(2 3 4 5)" {
		t.Errorf("Expected (cdr lst) to return (2 3 4 5), but got %s", result)
	}

	// cons operation
	result = EvaluateExpression(t, "(cons 0 lst)", env)
	if result != "(0 1 2 3 4 5)" {
		t.Errorf("Expected (cons 0 lst) to return (0 1 2 3 4 5), but got %s", result)
	}

	// null? operation
	result = EvaluateExpression(t, "(null? '())", env)
	if result != "#t" {
		t.Errorf("Expected (null? '()) to return #t, but got %s", result)
	}
}

// TestConditionalExpressions tests conditional expressions
func TestConditionalExpressions(t *testing.T) {
	env := typ.NewEnvironment()
	typ.DefineBuiltins(env)

	// if expression
	result := EvaluateExpression(t, "(if (> 5 3) \"yes\" \"no\")", env)
	if result != "\"yes\"" {
		t.Errorf("Expected (if (> 5 3) \"yes\" \"no\") to return \"yes\", but got %s", result)
	}

	// cond expression
	result = EvaluateExpression(t, "(cond ((< 1 0) \"negative\") ((> 1 0) \"positive\") (else \"zero\"))", env)
	if result != "\"positive\"" {
		t.Errorf("Expected cond expression to return \"positive\", but got %s", result)
	}
}

// TestLetExpressions tests let expressions
func TestLetExpressions(t *testing.T) {
	env := typ.NewEnvironment()
	typ.DefineBuiltins(env)

	// let expression
	result := EvaluateExpression(t, "(let ((a 1) (b 2)) (+ a b))", env)
	if result != "3" {
		t.Errorf("Expected (let ((a 1) (b 2)) (+ a b)) to return 3, but got %s", result)
	}
}

// TestErrorHandling tests error handling
func TestErrorHandling(t *testing.T) {
	env := typ.NewEnvironment()
	typ.DefineBuiltins(env)

	// Test division by zero error
	func() {
		defer func() {
			if r := recover(); r == nil {
				t.Errorf("Division by zero operation should raise an error")
			}
		}()
		
		// This should raise an error
		EvaluateExpression(t, "(/ 10 0)", env)
	}()
}